<template>
  <q-dialog v-model="modalVisible" :no-shake="!persistent" no-backdrop-dismiss no-esc-dismiss v-bind="modalBind"
            @keyup.esc="closeModal">
    <q-card ref="QModal" :style="cardStyle" class="q-modal-card" @click.stop>
      <q-bar v-drag="isMove" class="q-modal-header bg-transparent">
        <span class="text-h6 text-weight-medium text-primary ellipsis">{{ attrs.header }}</span>
        <q-space />
        <q-btn v-if="!nonFullBtn && !fullScreen && !attrs.position" :dense="isDense" :icon="fullBtn.icon"
               v-bind="btnBind" @click="toggle">
          <q-tooltip v-bind="$tooltipAttr">{{ fullBtn.text }}</q-tooltip>
        </q-btn>
        <q-btn v-if="attrs.closeBtn !== false" :dense="isDense" class="q-btn-close" icon="close" v-bind="btnBind"
               @click="closeModal">
          <q-tooltip v-bind="$tooltipAttr">{{ $t('Close') }}</q-tooltip>
        </q-btn>
      </q-bar>
      <div v-if="fullHeight" :class="classes || ''" :style="{ height: setMaxHeight() }" class="bg-white-6">
        <slot :height="maxHeight" name="default"></slot>
      </div>
      <div v-else-if="nonScrollArea" :class="classes || ''"
           :style="{ maxHeight: setMaxHeight(), height: height || 'auto' }" class="bg-white-6">
        <slot :maxHeight="maxHeight" name="default"></slot>
      </div>
      <q-scroll-area v-else ref="ScrollArea" :style="AreaStyle" :visible="false" class="bg-white-6">
        <div :class="classes || ''" class="scroll-modal__area q-pt-md q-pl-md q-pr-md q-pb-sm">
          <slot name="default"></slot>
          <q-resize-observer debounce="0" @resize="onResize" />
        </div>
      </q-scroll-area>
      <template v-if="menuBtn">
        <q-separator />
        <q-card-actions :align="btnPosition" class="q-modal-footer q-gutter-x-md q-px-md q-py-sm bg-transparent">
          <slot :done="done" :scope="menuBind" :startLoading="startLoading" name="menuLeft"></slot>
          <q-btn v-if="attrs.submitBtn !== false" color="primary" icon="published_with_changes" unelevated
                 v-bind="menuBind" @click="onConfirm">
            {{ attrs.submitText || $t('Submit') }}
          </q-btn>
          <q-btn v-if="attrs.closeBtn !== false" icon="cancel_schedule_send" outline text-color="primary"
                 v-bind="menuBind" @click="closeModal">
            {{ attrs.closeText || $t('Close') }}
          </q-btn>
          <slot :done="done" :scope="menuBind" :startLoading="startLoading" name="menuRight"></slot>
        </q-card-actions>
      </template>
      <q-inner-loading :showing="loading">
        <q-spinner-ios color="primary" size="50px" />
      </q-inner-loading>
    </q-card>
  </q-dialog>
</template>

<script setup>
import { dom, useQuasar } from 'quasar'
import { i18n } from 'src/boot/i18n'
import { computed, inject, nextTick, ref, useAttrs, watch } from 'vue'

const props = defineProps({
  height: String,
  classes: String,
  fullHeight: Boolean,
  fullScreen: Boolean,
  nonFullBtn: Boolean,
  nonScrollArea: Boolean,
  menuBtn: { type: Boolean, default: true },
  persistent: { type: Boolean, default: true }
})

const $q = useQuasar()
const isDense = inject('isDense', true)
const emits = defineEmits(['confirm', 'update:modelValue', 'update:loading'])

const attrs = useAttrs()
const QModal = ref(null)
const AreaStyle = ref({})
const maximized = ref(false)
const ScrollArea = ref(null)
const maxHeight = ref('100%')
const btnBind = { 'fab-mini': true, round: true, flat: true }
const loading = defineModel('loading', { type: Boolean, default: false })
const menuBind = ref({ 'no-wrap': true, padding: '.4rem 1rem', loading: loading.value })
const modalVisible = defineModel('modelValue', { type: Boolean, default: false })

const fullBtn = computed(() => {
  if (maximized.value) return { icon: 'fullscreen_exit', text: i18n.t('ExitFullScreen') }
  return { icon: 'zoom_out_map', text: i18n.t('FullScreen') }
})
const btnPosition = computed(() => attrs.btnAlign || 'right')
const isMove = computed(() => props.fullScreen || maximized.value ? false : !attrs.position)
const modalBind = computed(() => {
  const { fullWidth, position, class: classes = '' } = attrs, { fullHeight, fullScreen } = props
  return {
    class: `q-modal ${classes}`, fullWidth, persistent: props.persistent, position,
    fullHeight: fullHeight || ['left', 'right'].includes(position), maximized: fullScreen || maximized.value
  }
})
const cardStyle = computed(() => {
  if (props.fullScreen || maximized.value) return {}
  let width = '', minWidth = '', maxWidth = ''
  if (attrs.width) width = String(attrs.width).endsWith('%') ? attrs.width : attrs.width + 'px'
  if (attrs.minWidth) minWidth = String(attrs.minWidth).endsWith('%') ? attrs.minWidth : attrs.minWidth + 'px'
  if (attrs.maxWidth) maxWidth = String(attrs.maxWidth).endsWith('%') ? attrs.maxWidth : attrs.maxWidth + 'px'
  if (attrs.position) return { width: width || '35rem', minWidth, maxWidth: `${maxWidth} !important` }
  return { width: width || '45%', minWidth, maxWidth: `${maxWidth} !important` }
})

watch(modalVisible, () => initData())

const setMaxHeight = () => {
  const { position } = attrs, { fullScreen, menuBtn } = props
  let tempPX = 5.5
  if (fullScreen || maximized.value || position) tempPX -= 3
  if (!menuBtn) {
    maxHeight.value = `calc(100vh - ${tempPX}rem)`
    return maxHeight.value
  }
  const node = QModal.value.$el.querySelector('.q-modal-footer')
  maxHeight.value = `calc(100vh - ${tempPX}rem - ${node ? dom.height(node) + 1 : 0}px)`
  return maxHeight.value
}
const onResize = size => {
  if (!modalVisible.value) return
  AreaStyle.value.height = `${size.height}px`
  AreaStyle.value.maxHeight = setMaxHeight()
}
const toggle = () => maximized.value = !maximized.value
const exitModal = () => !props.persistent && closeModal()
const initData = () => {
  if (!modalVisible.value) return
  nextTick(() => {
    const NODE = QModal.value?.$el?.parentNode?.parentNode
    if ($q.platform.has.touch) NODE && !NODE.ontouchend && (NODE.ontouchend = exitModal)
    else NODE && !NODE.onclick && (NODE.onclick = exitModal)
  })
}
const done = isClose => {
  loading.value = false
  if (isClose === false) return
  closeModal()
}
const startLoading = () => loading.value = true
const onConfirm = () => {
  startLoading()
  emits('confirm', done)
}
const closeModal = () => {
  const beforeClose = attrs.onBeforeClose || attrs.beforeClose
  if (beforeClose && typeof beforeClose === 'function') beforeClose()
  else modalVisible.value = false
}
</script>